
#include "Impl_Render.h"

_BeginNamespace(Radix)
_BeginNamespace(Engine)
_BeginNamespace(Component)
_BeginNamespace(Direct3D)

/*
=================================================================
== Geometry
=================================================================
*/
Geometry::Geometry(ID3D11Buffer* vertexBuffer, ID3D11Buffer* indexBuffer)
	:mVertexBuffer(vertexBuffer),
	mIndexBuffer(indexBuffer)
{}

 Geometry::~Geometry()
 {
	 if(mVertexBuffer != nullptr)
	 {
		 mVertexBuffer->Release();
		 mVertexBuffer = nullptr;
	 }
	 if(mIndexBuffer != nullptr)
	 {
		 mIndexBuffer->Release();
		 mIndexBuffer = nullptr;
	 }
 }
 /*
=================================================================
== RenderObject
=================================================================
*/
RenderObject::RenderObject(const Matrix4x4& t, const IGeometry& g, const IMaterial& m)
	:mTransform(t),
	mGeometry(g),
	mMaterial(m)
{}

RenderObject::~RenderObject()
{}

const Matrix4x4& RenderObject::transform()
{
	return mTransform;
}

void RenderObject::transform(const Matrix4x4& t)
{
	mTransform = t;
}

const IGeometry& RenderObject::geometry()
{
	return mGeometry;
}

void RenderObject::geometry(const IGeometry& g)
{
	mGeometry = g;
}

const IMaterial& RenderObject::material()
{
	return mMaterial;
}

void RenderObject::material(const IMaterial& m)
{
	mMaterial = m;
}
/*
=================================================================
== RenderSystem
=================================================================
*/
RenderSystem::RenderSystem()
	:mDevice(nullptr),
	mImmContext(nullptr),
	mSwapChain(nullptr),
	mRenderTargetView(nullptr),

{}

RenderSystem::~RenderSystem()
{}

const String& RenderSystem::name()
{
	static const String skName = _T("Component_Render_Direct3D");
	return skName;
}

bool RenderSystem::init(const StringValueMap& params)
{
	StringValueMap configs = params;

	HWND windowHandle = (HWND)(uint)configs[_T("RenderWindowHandle")];
	int windowWidth = configs[_T("RenderWindowWidth")];
	int windowHeight = configs[_T("RenderWindowHeight")];
	bool isFullScreen = configs[_T("IsFullScreen")];

	DXGI_SWAP_CHAIN_DESC scd;
	memset(&scd, 0, sizeof(scd));
	scd.BufferCount = 1;
	scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	scd.BufferDesc.Width = windowWidth;
	scd.BufferDesc.Height = windowHeight;
	scd.BufferDesc.RefreshRate.Numerator = 60;
	scd.BufferDesc.RefreshRate.Denominator = 1;
	scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	scd.SampleDesc.Count = 1;
	scd.SampleDesc.Quality = 0;
	scd.OutputWindow = windowHandle;
	scd.Windowed = !isFullScreen;
	if(FAILED(D3D11CreateDeviceAndSwapChain(
		nullptr,
		D3D_DRIVER_TYPE_HARDWARE, 0,
		D3D11_CREATE_DEVICE_SINGLETHREADED,
		nullptr, 0,
		D3D11_SDK_VERSION,
		&scd,
		&mSwapChain, &mDevice, nullptr, &mImmContext)))
	{
		return false;
	}

	ID3D11Texture2D* backBuffer = nullptr;
	if(FAILED(mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer)))
		return false;
	if(FAILED(mDevice->CreateRenderTargetView(backBuffer, nullptr, &mRenderTargetView)))
		return false;
	backBuffer->Release();
	mImmContext->OMSetRenderTargets(1, &mRenderTargetView, nullptr);

	D3D11_VIEWPORT viewport;
	viewport.TopLeftX = 0.0f;
	viewport.TopLeftY = 0.0f;
	viewport.Width = windowWidth+0.0f;;
	viewport.Height = windowHeight+0.0f;
	viewport.MinDepth = 0.0f;
	viewport.MaxDepth = 1.0f;
	mImmContext->RSSetViewports(1, &viewport);

	return true;
}

void RenderSystem::exit()
{
	if(mRenderTargetView != nullptr)
	{
		mRenderTargetView->Release();
		mRenderTargetView = nullptr;
	}
	if(mSwapChain != nullptr)
	{
		mSwapChain->Release();
		mSwapChain = nullptr;
	}
	if(mImmContext != nullptr)
	{
		mImmContext->Release();
		mImmContext = nullptr;
	}
	if(mDevice != nullptr)
	{
		mDevice->Release();
		mDevice = nullptr;
	}
}

IGeometry* RenderSystem::createGeometry(const Mesh& mesh)
{
	ID3D11Buffer* vertexBuffer = nullptr;
	ID3D11Buffer* indexBuffer = nullptr;

	// create vertex buffer.
	D3D11_BUFFER_DESC vbd;
	ZeroMemory(&vbd, sizeof(vbd));
	vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	vbd.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
	vbd.Usage = D3D11_USAGE_DEFAULT;
	vbd.ByteWidth = sizeof(Mesh::Vertex) * mesh.vcount();

	D3D11_SUBRESOURCE_DATA vdata;
	ZeroMemory(&vdata, sizeof(vdata));
	vdata.pSysMem = mesh.vdata();

	if(FAILED(mDevice->CreateBuffer(&vbd, &vdata, &vertexBuffer)))
		return nullptr;

	// create index buffer.
	D3D11_BUFFER_DESC ibd;
	ZeroMemory(&ibd, sizeof(ibd));
	ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	ibd.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
	ibd.Usage = D3D11_USAGE_DEFAULT;
	ibd.ByteWidth = sizeof(Mesh::Triangle) * mesh.tcount();

	D3D11_SUBRESOURCE_DATA idata;
	ZeroMemory(&idata, sizeof(idata));
	idata.pSysMem = mesh.tdata();

	if(FAILED(mDevice->CreateBuffer(&ibd, &idata, &indexBuffer)))
	{
		vertexBuffer->Release();
		return nullptr;
	}

	IGeometry* geometry = new Geometry(vertexBuffer, indexBuffer);
	return geometry;
}

void RenderSystem::dispatchObject(const IRenderObject& object)
{
	mRenderObjects.push_back(object);
}

void RenderSystem::renderFrame(real elapsed)
{
	float clearColor[4] = {0.0f, 0.0f, 0.0f, 0.0f};
	mImmContext->ClearRenderTargetView(mRenderTargetView, clearColor);
	
	auto objectIter = mRenderObjects.begin();
	while(objectIter != mRenderObjects.end())
	{
		// Todo: draw object.
		++ objectIter;
	}
	mRenderObjects.clear();

	mSwapChain->Present(0, 0);
}

UINT RenderSystem::translateCpuAccess(RenderResourceCpuAccess cpuAccess)
{
	UINT result = 0;
	switch(cpuAccess)
	{
	case eRenderResourceCpuAccess_ReadOnly:
		result = D3D11_CPU_ACCESS_READ;
		break;
	case eRenderResourceCpuAccess_WriteOnly:
		result = D3D11_CPU_ACCESS_WRITE;
		break;
	case eRenderResourceCpuAccess_ReadWrite:
		result = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
	}
	return result;
}

D3D11_USAGE RenderSystem::translateUsage(RenderResourceUsage usage)
{
	D3D11_USAGE result = D3D11_USAGE_DEFAULT;
	switch(usage)
	{
	case eRenderResourceUsage_Default:
		result = D3D11_USAGE_DEFAULT;
		break;
	case eRenderResourceUsage_Immutable:
		result = D3D11_USAGE_IMMUTABLE;
		break;
	case eRenderResourceUsage_Dynamic:
		result = D3D11_USAGE_DYNAMIC;
		break;
	case eRenderResourceUsage_Staging:
		result = D3D11_USAGE_STAGING;
		break;
	}
	return result;
}

_EndNamespace(Direct3D)
_EndNamespace(Component)
_EndNamespace(Engine)
_EndNamespace(Radix)
